//
// gcssai.cpp
// gcs
// Grand Central Station, controlling process of the Protea Application Server / Business Logic Transactions for SAI
// Copyright (C) 2003, 2004, 2005 eXegeSys, Inc.
// Copyright (C) 2008 Bruce A. James
//
// This program is part of the Protea Project.
//
// The Protea Project is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the 
// Free Software Foundation, either version 3 of the License, or (at your 
// option) any later version.
//
// The Protea Project is distributed in the hope that it will be useful, but 
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
// for more details.
// 
// You should have received a copy of the GNU General Public License along 
// with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// Please send correspondence to:
// 
// theproteaproject@gmail.com
//

// gcssai.cpp
//
// Contains the SAI transactions, compiled into GCS
//

#include "../SDK/ToolsInterface.h"
#include "../AppHandler/parms_xml.h"
#include "gcs.h"

//@ MAN Protea System Administration Interface

//@ COP // theproteaproject@gmail.com

//@ SCT 1 Database Definitions
//@ SCT 2 Transaction Descriptions
//@ SCT 3 Subtransaction Descriptions
// SCT 4 Batch Job Transaction Descriptions
// SCT 5 Utility Transaction Descriptions

//////////////////////////////////////////////////////////////////////////////
//
// Transaction Case Statement
//
//////////////////////////////////////////////////////////////////////////////

XCTransactionInfo* XCGCS::OnTransaction( XCTransactionInfo* txn )
{
	INFUNC( XCGCS::OnTransaction, this );

	if ((txn->GetHeader()->GetHeaderPtr()->miscflag & misc_query) == 0) {
		HandleTransactionResponse( txn );
		return NULL;
	};
/*
	printf( "***********************************\n" );
	printf( "GCS handling SAI transaction: %ld\n", txn->GetTransNumber() );
	printf( "***********************************\n" );
*/
	XCTransactionInfo* reply = NULL;
	switch( txn->GetTransNumber() )
	{
		case TRANS_REVALLPROCESS:	reply = RevAllProcess( txn );		break;
		case TRANS_PROCESSDETAIL:	reply = ProcessDetail( txn );		break;
		case TRANS_SETTRACELEVEL:	reply = SetTraceLevel( txn );		break;
		case TRANS_SETLISTENERPORT:	reply = SetListenerPort( txn );		break;
		case TRANS_UPDATEAPPL:		reply = UpdateApplication( txn );	break;
//		case TRANS_UPDATEINT:		reply = UpdateInterface( txn );		break;
		case TRANS_SHUTDOWN:		reply = Shutdown( txn );			break;
		case TRANS_REVIEWLICENSE:	reply = ReviewLicenses( txn );		break;

		//
		// SECURITY
		//
		case TRANS_ADD_USER:		reply = AddUser( txn );				break;
		case TRANS_CHG_USER:		reply = ChangeUser( txn );			break;
		case TRANS_CHG_PASSWORD:	reply = ChangeUser( txn ); break;
		case TRANS_DEL_USER:		reply = RemoveUser( txn );			break;
		case TRANS_REVIEW_USER:		reply = ReviewUser( txn );			break;
		case TRANS_REVIEW_USERS:	reply = ListUsers( txn );			break;

		case TRANS_ADD_GROUP:		reply = AddGroup( txn );			break;
		case TRANS_DEL_GROUP:		reply = RemoveGroup( txn );			break;
		case TRANS_REVIEW_GROUP:	reply = ReviewGroup( txn );			break;
		case TRANS_REVIEW_GROUPS:	reply = ListGroups( txn );			break;

		case TRANS_ADD_USER_TO_GROUP:	reply = AddUserToGroup( txn );	break;
		case TRANS_DEL_USER_FROM_GROUP:	reply = RemoveUserFromGroup( txn ); break;

		case TRANS_REVIEW_APPLICATIONS:		reply = ListApplications( txn ); break;
		case TRANS_REVIEW_APPLICATION:		reply = ReviewApplication( txn ); break;
		case TRANS_REVIEW_APPL_SUB_ITEM:	reply = ReviewApplicationSubItem( txn ); break;

//		case TRANS_ADD_APP_SECURITY:			reply = AddAppSecurity( txn );	break;
//		case TRANS_DEL_APP_SECURITY:			reply = DelAppSecurity( txn );	break;
//		case TRANS_CHG_APP_SECURITY:			reply = ChgAppSecurity( txn );	break;

		case TRANS_ADD_APP_SECURITY_USER:		reply = AddAppSecurityUser( txn );	break;
		case TRANS_DEL_APP_SECURITY_USER:		reply = DelAppSecurityUser( txn );	break;
		case TRANS_CHG_APP_SECURITY_USER:		reply = ChgAppSecurityUser( txn ); break;

		// Logs
		case TRANS_REVIEW_ADMIN_LOGS:		reply = ReviewAdminLogs( txn );			break;

		// General statistics
		case TRANS_REVIEW_GENERAL_STATS:	reply = ReviewGeneralStats( txn );		break;

		// JOBS
		case TRANS_ADD_JOB:					reply = AddJob( txn );					break;
		case TRANS_DEL_JOB:					reply = DeleteJob( txn );				break;
		case TRANS_CHG_JOB:					reply = ChangeJob( txn );				break;
		case TRANS_REV_JOB_SCHEDULE:		reply = ReviewJobSchedule( txn );		break;
		default:
			RAISETOOLSERROR( ERR_BAD_TXN );
	};	// switch (transaction)

	// modify src_app to src_gcs and dest_int to dest_app before sending...
	// ( so the message will move through dispatch to sai, not to interface )
	XCMessageList* temp = reply->GetFirstMessage();
	base_message* hdr = NULL;
	while ( temp )
	{
		hdr = temp->msg->GetHeaderPtr();
		temp->msg->SetHeader( hdr->type, src_gcs, dest_app, false, false );
		temp = temp->next;
	};
	
	OUTFUNCRET( reply );
};

//////////////////////////////////////////////////////////////////////////////
//
// Transaction Code ( SAI )
//
//////////////////////////////////////////////////////////////////////////////


//@ SEC 2
//@ CHA Shutdown
//@ COD Shutdown
//@ NUM 1
TRANSACTION_CODE( XCGCS::Shutdown )
{
	START_TXN( Shutdown );

		// Signal shutdown of the system
		shutdown = true;

		ToolsTrace( TRC_APPINTERFACE, TLV_LOW, "Sending shutdown command to GCS..." );

		XCToolsMessage msg_send( this );
		disp_message* disp_info;

		msg_send.CreateMsg( DISP_MESSAGE_SIZE );
		msg_send.SetHeader( msg_dispatch, src_gcs, dest_all, true, false );
		disp_info = (disp_message*)msg_send.GetDataPtr();
		disp_info->opcode = disp_shutdown;
		BroadcastMessage( msg_src, &msg_send );

	CLEANUP_TXN();
	END_TXN();
};





//@ SEC 2
//@ CHA Rev All Process
//@ COD RevAllProcess
//@ NUM 1
#define SetNumberField( fldname, value )  fld=detail->GetField( fldname );  if (fld) { sprintf( number, "%ld", value ); fld->SetString( number ); };

void XCGCS::GetProcessInfo( XCBuffer* detail, XCProcessStats* temp, double currenttime ) {
	char tempstr[ 40 ];
	char number[ 20 ];
	XCField* fld;

	fld = detail->GetField( FLD_HOST_NAME );
	if (temp->host)
		fld->SetString( temp->host );
	else
		fld->SetString( "UNKNOWN" );

	fld = detail->GetField( FLD_FILE_NAME );
	if (temp->filename)
		fld->SetString( temp->filename );
	else
		fld->SetString( "UNKNOWN" );

	SetNumberField( FLD_PID, temp->pid );
	SetNumberField( FLD_PARENT_PID, temp->parentpid );
	SetNumberField( FLD_LISTENER, temp->listenport );
	SetNumberField( FLD_TRANS_COMPLETE, temp->trans_complete );
	SetNumberField( FLD_TRANS_OPEN, temp->trans_open );
	SetNumberField( FLD_TRANS_STATED, temp->trans_stated );
	SetNumberField( FLD_MSG_SENT, temp->msg_sent );
	SetNumberField( FLD_MSG_RECV, temp->msg_recv );

	fld = detail->GetField( FLD_CPU_TIME );
	if (fld) {
		if (temp->cpu_time)
			sprintf( tempstr, "%5.2f", temp->cpu_time );
		else
			strcpy( tempstr, "----" );
		fld->SetString( tempstr );
	};

	fld = detail->GetField( FLD_LAST_CHECK );
	if (fld) {
		if (temp->last_check) {
			double time = currenttime - temp->last_check;
			sprintf( tempstr, "%5.2f", time );
			fld->SetString( tempstr );
		} else
			fld->SetString( "----" );
	};
};




TRANSACTION_CODE ( XCGCS::RevAllProcess )
{
	XCBuffer* detail = NULL;

	START_TXN( RevAllProcess );
		// Send pushdown query
		XCToolsMessage* message = new XCToolsMessage( this );
		message->CreateMsg( DISP_MESSAGE_SIZE );
		message->SetHeader( msg_dispatch, src_gcs, dest_all, true, false );

		disp_message* disp_info = (disp_message*)message->GetDataPtr();
		disp_info->opcode = disp_pushdown_query;

		BroadcastMessage( msg_src, message );

		message->Release();

		int child = txnvars.buffer->GetChildFormat();
		if ( child )
		{
			double currenttime = timer.GetCurrent();
			detail = CreateBuffer( child, false );

			//@ GRS Loop through each substation
			XCSubstation* sub = hostlist.GetFirstHost();
			while (sub) {
				// Output stats on this substation
				GetProcessInfo( detail, sub, currenttime );
				TransResponse( txnvars.result, detail, TRANS_DETAIL_TYPE );

				// Output stats on each application under this interface
				XCApplication* app = sub->applist;
				while (app) {
					GetProcessInfo( detail, app, currenttime );
					TransResponse( txnvars.result, detail, TRANS_DETAIL_TYPE );
					app = app->next_in_sub_list;
				};
				sub = sub->next;
			};


			// Lastly, output interface statistics
			XCInterface* iface = hostlist.GetFirstInterface();
			while (iface) {
				GetProcessInfo( detail, iface, currenttime );
				TransResponse( txnvars.result, detail, TRANS_DETAIL_TYPE );

				iface = iface->next;
			};
		};
		//@ GRE

		txnvars.result->SetSuccess( true );

	CLEANUP_TXN();
		CleanupObjects( 1, &detail );			
	END_TXN();
};



//@ CHA Process Detail
//@ COD ProcessDetail
//@ NUM 2
TRANSACTION_CODE( XCGCS::ProcessDetail ) {
	XCBuffer* detail = NULL;
	bool found = false;

	START_TXN( ProcessDetail );
		// Send pushdown query
		XCToolsMessage* message = new XCToolsMessage( this );
		message->CreateMsg( DISP_MESSAGE_SIZE );
		message->SetHeader( msg_dispatch, src_gcs, dest_all, true, false );

		disp_message* disp_info = (disp_message*)message->GetDataPtr();
		disp_info->opcode = disp_pushdown_query;

		BroadcastMessage( msg_src, message );

		message->Release();

		// Verify that we have a PID
		XCField* pid = txnvars.buffer->GetField( FLD_PID );
		if (!pid || pid->IsBlank())
			txnvars.Errors->Add( ERR_PID_NOT_PRESENT, SEV_CRITICAL, FLD_PID, 1, "" );

		// Verify that we have a host name
		XCField* host = txnvars.buffer->GetField( FLD_HOST_NAME );
		if (!host || host->IsBlank())
			txnvars.Errors->Add( 1, SEV_CRITICAL );

		// Attempt to locate the host in the list of known hosts
		XCSubstation* sub = hostlist.FindHost( host->GetStringPtr() );
		XCProcessStats* app = NULL;
		if (sub) {
			app = sub->FindPID( atoi( pid->GetStringPtr() ) );
		} else {
			// It may be an interface, and may not be running under a substation...
			int pid_nbr = atoi( pid->GetStringPtr() );

			XCInterface* inter = hostlist.GetFirstInterface();
			while (inter && inter->pid != pid_nbr) {
				inter = inter->next;
			};

			if (inter)
				app = inter;
			else
				txnvars.Errors->Add( 1, SEV_CRITICAL );
		};

		if (app) {
			found = true;

			// Grab status information
			GetProcessInfo( txnvars.buffer, app, timer.GetCurrent() );
			TransResponse( txnvars.result, txnvars.buffer, TRANS_HEADER_TYPE );

			// Use child forms for debug information
			int child = txnvars.buffer->GetChildFormat();
			if ( child ) {
				detail = CreateBuffer( child );
				int level = 1;
				for (long i=0; i<num_tc; i++) {
					XCField* fld;
					char number[ 10 ];
					SetNumberField( FLD_FILTER_TYPE, i );
					SetNumberField( FLD_TRACING_ON, (app->debug_type_filter & level)>0 ? (long)1:(long)0 );
					SetNumberField( FLD_LEVEL_CUTOFF, (long)app->debug_level_cutoff[ i ] );
					level *= 2;
					TransResponse( txnvars.result, detail, TRANS_DETAIL_TYPE );
				};
			};
		};
		if (!found)
			txnvars.Errors->Add( ERR_PID_NOT_PRESENT, SEV_CRITICAL, FLD_PID, 1, pid->GetStringPtr() );
		txnvars.result->SetSuccess( true );

	CLEANUP_TXN();
		CleanupObjects( 1, &detail );
	END_TXN();
};




//@ CHA Set Trace Level
//@ COD SetTraceLevel
//@ NUM 2
TRANSACTION_CODE ( XCGCS::SetTraceLevel )
{
	XCToolsMessage* message = NULL;

	XCField* fld;

	START_TXN( SetTraceLevel );

		// Verify that we have a PID
		XCField* pid = txnvars.buffer->GetField( FLD_PID );
		if (!pid || pid->IsBlank())
			txnvars.Errors->Add( ERR_PID_NOT_PRESENT, SEV_CRITICAL, FLD_PID, 1, "" );

		// Verify that we have a host name
		XCField* host = txnvars.buffer->GetField( FLD_HOST_NAME );
		if (!host || host->IsBlank())
			txnvars.Errors->Add( 1, SEV_CRITICAL );

		// Attempt to locate the host in the list of known hosts
		XCSubstation* sub = hostlist.FindHost( host->GetStringPtr() );
		if (!sub)
			txnvars.Errors->Add( 1, SEV_CRITICAL );

		XCProcessStats* app = sub->FindPID( atoi( pid->GetStringPtr() ) );
		if (!app)
			txnvars.Errors->Add( ERR_PID_NOT_PRESENT, SEV_CRITICAL, FLD_PID, 1, pid->GetStringPtr() );

		txnvars.Errors->Check();

		//@ Create a msg_options message
		ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Creating Options Message... %s", app->host );
		message = new XCToolsMessage( this );
		message->CreateMsg( OPTION_MESSAGE_SIZE + strlen( app->host ) + 1 );
		message->SetHeader( msg_options, src_gcs, dest_all, true, false );
		option_message* opt = (option_message*)message->GetDataPtr();

		//@ Move tracing values from transaction buffer to the message
		ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Setting Options Message Values..." );
		fld = txnvars.buffer->GetField( FLD_PID );
		opt->dest_pid = atol( fld->GetOffset() );

		fld = txnvars.buffer->GetField( FLD_FILTER_TYPE );
		opt->debug_type_filter = atoi( fld->GetOffset() );

		fld = txnvars.buffer->GetField( FLD_LEVEL_CUTOFF );
		opt->debug_level_cutoff = atoi( fld->GetOffset() );

		fld = txnvars.buffer->GetField( FLD_TRACING_ON );
		opt->enabled = atoi( fld->GetOffset() );

		strcpy( opt->hostname, app->host );

		//@ Broadcast the message to all children processes
		BroadcastMessage( msg_src, message );

		//@ Reply with success
		ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "Transaction complete..." );
		txnvars.result->SetSuccess( true );

	CLEANUP_TXN();
		CleanupObjects( 1, &message );
	END_TXN();
};

//@ CHA Set Listener Port
//@ COD SetListenerPort
//@ NUM 3
TRANSACTION_CODE( XCGCS::SetListenerPort )
{
	START_TXN( SetListenerPort );
/*
		//@ Verify that port number is not blank.  If it is, display
		//@ ERR C CANNOT_BE_BLANK (Field Name) cannot be blank
    	XCField* temp = txnvars.buffer->GetField( FLD_PORT_NBR );
		if ( temp->IsBlank() )
			txnvars.Errors->Add( ERR_CANNOT_BE_BLANK, SEV_CRITICAL, FLD_PORT_NBR, 1, temp->GetFieldName() );
        txnvars.Errors->Check();

		//@ Verify that the new port number is > 1023 and < 65536.  If it isn't, display
		//@ ERR C PORT_OUT_OF_RANGE Port number is out of range ( 1024-65535 )
		if ( !temp->CheckInRange( 1024, 65535 ) )
			txnvars.Errors->Add( ERR_PORT_OUT_OF_RANGE, SEV_CRITICAL, FLD_PORT_NBR );
		txnvars.Errors->Check();

		//@ SUB SetPort to set the new port number
		SetPort( temp->GetStringPtr() );

		//@ SUB SaveConfig to save changes
		SaveConfig();

		//@ *** Add the following functionality at a later date
		//@ *** Close current listener port
		//@ *** Open new listener port
		txnvars.result->SetSuccess( true );
*/
	CLEANUP_TXN();
	END_TXN();
};

//@ CHA Update Application
//@ COD UpdateAppl
//@ NUM 4
TRANSACTION_CODE( XCGCS::UpdateApplication )
{
	START_TXN( UpdateApplication );
		//@ Verify that application number is not blank.  If it is, display
		//@ ERR E CANNOT_BE_BLANK (Field Name) cannot be blank
    	XCField* temp = txnvars.buffer->GetField( FLD_APPL_NBR );
		if ( temp->IsBlank() )
			txnvars.Errors->Add( ERR_CANNOT_BE_BLANK, SEV_ERROR, FLD_APPL_NBR, 1, temp->GetFieldName() );

		//@ Verify that file name is not blank.  If it is, display
		//@ ERR E CANNOT_BE_BLANK (Field Name) cannot be blank
    	temp = txnvars.buffer->GetField( FLD_FILE_NAME );
		if ( temp->IsBlank() )
			txnvars.Errors->Add( ERR_CANNOT_BE_BLANK, SEV_ERROR, FLD_FILE_NAME, 1, temp->GetFieldName() );

		//@ Verify that host name is not blank.  If it is, display
		//@ ERR E CANNOT_BE_BLANK (Field Name) cannot be blank
    	temp = txnvars.buffer->GetField( FLD_HOST_NAME );
		if ( temp->IsBlank() )
			txnvars.Errors->Add( ERR_CANNOT_BE_BLANK, SEV_ERROR, FLD_HOST_NAME, 1, temp->GetFieldName() );
		txnvars.Errors->Check();

		//@ If an application record is not found, display
		//@ ERR E APPL_NOT_PRESENT Application (APPL_NBR) does not exist in this system
		if ( 1 )
		{
			temp = txnvars.buffer->GetField( FLD_APPL_NBR );
			txnvars.Errors->Add( ERR_APPL_NOT_PRESENT, SEV_ERROR, FLD_APPL_NBR, 1, temp->GetLong( true ) );
			txnvars.Errors->Check();
		}
/*
		//@ Update application record
		if ( tempapp->machine )
			delete[] tempapp->machine;
		tempapp->machine = strdup( txnvars.buffer->GetField( FLD_HOST_NAME )->GetStringPtr() );
		if ( tempapp->name )
			delete[] tempapp->name;
		tempapp->name = strdup( txnvars.buffer->GetField( FLD_FILE_NAME )->GetStringPtr() );
*/
		//@ SUB SaveConfig to save changes
		SaveConfig();

		//@ *** Add the following functionality at a later date
		//@ *** Update load balancing parameters (min/max users, sched. start/stop times, etc.)
		//@ SUB SaveConfig to save changes
		txnvars.result->SetSuccess( true );
	CLEANUP_TXN();
	END_TXN();
};

//@ CHA Update Interface
//@ COD UpdateInt
//@ NUM 5
TRANSACTION_CODE( XCGCS::UpdateInterface )
{
	START_TXN( UpdateInterface );
		//@ Verify that file name is not blank.  If it is, display
		//@ ERR E CANNOT_BE_BLANK (Field Name) cannot be blank
    	XCField* temp = txnvars.buffer->GetField( FLD_FILE_NAME );
		if ( temp->IsBlank() )
			txnvars.Errors->Add( ERR_CANNOT_BE_BLANK, SEV_ERROR, FLD_FILE_NAME, 1, temp->GetFieldName() );

		//@ Verify that host name is not blank.  If it is, display
		//@ ERR E CANNOT_BE_BLANK (Field Name) cannot be blank
    	temp = txnvars.buffer->GetField( FLD_HOST_NAME );
		if ( temp->IsBlank() )
			txnvars.Errors->Add( ERR_CANNOT_BE_BLANK, SEV_ERROR, FLD_HOST_NAME, 1, temp->GetFieldName() );

		//@ Verify that port number is not blank.  If it is, display
		//@ ERR E CANNOT_BE_BLANK (Field Name) cannot be blank
    	temp = txnvars.buffer->GetField( FLD_PORT_NBR );
		if ( temp->IsBlank() )
			txnvars.Errors->Add( ERR_CANNOT_BE_BLANK, SEV_ERROR, FLD_PORT_NBR, 1, temp->GetFieldName() );
		txnvars.Errors->Check();

		//@ Verify that the new port number is > 1023 and < 65536.  If it isn't, display
		//@ ERR E PORT_OUT_OF_RANGE Port number is out of range ( 1024-65535 )
		if ( !temp->CheckInRange( 1024, 65535 ) )
			txnvars.Errors->Add( ERR_PORT_OUT_OF_RANGE, SEV_ERROR, FLD_PORT_NBR );
		txnvars.Errors->Check();
/*
		//@ Find interface record in interface startup list
		XCStartInterface* tempint = interfacelist;
		char* intname = (char*) txnvars.buffer->GetField( FLD_FILE_NAME )->GetStringPtr();
		while ( tempint && strcmp( intname, tempint->name ) )
			tempint = tempint->next;

		//@ If an interface record is not found, display
		//@ ERR C INT_NOT_REPRESENT Interface (INTERFACE_NAME) does not exist in this system
		if ( !tempint )
		{
			temp = txnvars.buffer->GetField( FLD_FILE_NAME );
			txnvars.Errors->Add( ERR_INT_NOT_PRESENT, SEV_CRITICAL, FLD_FILE_NAME, 1, temp->GetStringPtr() );
			txnvars.Errors->Check();
		}

		//@ Update machine name
		if ( tempint->machine )
			delete[] tempint->machine;
		tempint->machine = strdup( txnvars.buffer->GetField( FLD_HOST_NAME )->GetStringPtr() );

		//@ Update listener port number
		tempint->port = atol( txnvars.buffer->GetField( FLD_PORT_NBR )->GetStringPtr() );
*/
		//@ SUB SaveConfig to save changes
		SaveConfig();

		txnvars.result->SetSuccess( true );
	CLEANUP_TXN();
	END_TXN();
};


//////////////////////////////////////////////////////////////////////////////





//@ SEC 2
//@ CHA ReviewLicenses
//@ COD ReviewLicenses
//@ NUM 15
TRANSACTION_CODE( XCGCS::ReviewLicenses )
{
	XCBuffer* detail = NULL;
	XCBuffer* trans_defn = NULL;
	XCBuffer* appl_defn = NULL;
	XCDBStatement* stmt = NULL;

	START_TXN( ReviewLicenses );
		START_DB_TXN();

			XCSQLGen* query = txnvars.query;

			int child = txnvars.buffer->GetChildFormat();
			if ( child )
			{
				trans_defn = CreateBuffer( FMT_TRANSACTION_DEFN, false );
				appl_defn = CreateBuffer( FMT_APPL_DEFN, false );
				detail = CreateBuffer( child, false );

				// Get the current time
				time_t current_time = time( NULL );

				// Refresh the licensing tables
				licensing.Refresh( current_time );

				XCField* trans_code = detail->GetField( FLD_TRANS_CODE );
				XCField* db_trans_code = trans_defn->GetField( FLD_TRANS_CODE );

				// Output Data
				licensing.session_lock.Lock();
				try {
					const GCSSession* data = licensing.GetFirstSession();
					while (data) {
						// Clear the detail buffer
						detail->Clear();

						// Get data
						detail->GetField( FLD_USER_NAME )->SetString( data->GetUser() );

						// Write the transaction number
						int last_app = data->GetLastApp();
						if (last_app) {
							if (last_app == GCS_APP_NUMBER)
								last_app = 1;

							trans_defn->GetField( FLD_APPL_NBR )->SetLong( last_app );
							trans_defn->GetField( FLD_TRANS_NUMBER )->SetLong( data->GetLastTrans() );

							query->MakeFull( SQL_STMT_SELECT, trans_defn );
							stmt = txnvars.dbtxn->ExecuteSQL( query );
							if (!stmt->Eof())
								trans_code->MoveField( db_trans_code );
							else
								trans_code->Clear();
							stmt->Release();
							stmt = NULL;
						} else
							trans_code->Clear();

						// Write the appl number
						appl_defn->GetField( FLD_APPL_NBR )->SetLong( last_app );
						query->MakeFull( SQL_STMT_SELECT, appl_defn );
						stmt = txnvars.dbtxn->ExecuteSQL( query );
						if (!stmt->Eof())
							detail->MoveField( appl_defn, FLD_APPL_NAME );
						else
							detail->GetField( FLD_APPL_NAME )->Clear();
						stmt->Release();
						stmt = NULL;

						// Send the response
						TransResponse( txnvars.result, detail, TRANS_DETAIL_TYPE );

						// Move on to next record
						data = data->next;
					};
				} catch( ... ) {
					licensing.session_lock.UnLock();
					throw;
				};
				licensing.session_lock.UnLock();
			};

		END_DB_TXN();

		txnvars.result->SetSuccess( true );

	CLEANUP_TXN();
		CleanupObjects( 4, &detail, &trans_defn, &stmt, &appl_defn );
	END_TXN();
};



TRANSACTION_CODE( XCGCS::PrepApplication )
{
	START_TXN( PrepApplication )

		char path[ MAX_PATH ];
		sprintf( path, "%s%sTools%sprep 40", root, OSFILEDELIM, OSFILEDELIM );
		if (!XCShellExecute( path ))
			txnvars.result->SetSuccess( true );

	CLEANUP_TXN();
	END_TXN();
};
